package org.knot.mockejb3.createors;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.hibernate.SessionFactory;
import org.knot.mockejb3.EjbBeanAccess;
import org.knot.mockejb3.MockContainer;
import org.knot.mockejb3.SessionBeanDescriptor;
import org.knot.mockejb3.annotation.AnnotationRule;
import org.knot.mockejb3.annotation.AnnotationRuleManager;
import org.knot.mockejb3.annotation.ClassAnnotation;
import org.knot.mockejb3.annotation.RuleParams;
import org.knot.mockejb3.annotation.StateFulBeanManager;
import org.knot.mockejb3.interceptor.AspectSystem;
import org.knot.mockejb3.interceptor.AspectSystemFactory;
import org.knot.mockejb3.interceptor.ClassPatternPointcut;
import org.knot.mockejb3.jndi.MockContextFactory;
import org.knot.mockejb3.transcation.TransactionManager;
import org.knot.mockejb3.transcation.TransactionPolicy;


/**
 * 
 * 获取EJB3 SessionBean
 * 
 * @author yuxiaowei
 * 
 */
@SuppressWarnings("unchecked")
public class SessionBeanFactory {

    private static List<String> listPointcut = new ArrayList<String>();

    private static SessionBeanFactory me;

    private AspectSystem aspectSystem;

    private org.knot.mockejb3.plugin.hibernate.MockUserTransaction mockTransaction;

    Context context = null;

    MockContainer mockContainer = null;
    
    //是否需要加入事务
    static boolean isMockTransaction = true;

    private SessionBeanFactory() throws NamingException {
        super();
        
        Map<String, SessionFactory> mapSF =  LoadConf.getMapSessionFactory();
        
        aspectSystem = AspectSystemFactory.getAspectSystem();

        //设置系统JNDI的参数；
        MockContextFactory.setAsInitial();
        context = new InitialContext();
        mockContainer = new MockContainer(context);

        if (null!=mapSF)
        {
            List<SessionFactory> listSf = new ArrayList<SessionFactory>();
            for (Map.Entry<String, SessionFactory> sf : mapSF.entrySet()) {
                context.rebind(sf.getKey(), sf.getValue());
                listSf.add(sf.getValue());
            }
            mockTransaction = new org.knot.mockejb3.plugin.hibernate.MockUserTransaction(listSf.toArray(new SessionFactory[0]));
            context.rebind("javax.transaction.UserTransaction", mockTransaction);
        }
        else
        {
            isMockTransaction = false;
        }
    }
    

    // singleton pattern
    public static SessionBeanFactory getInstance() throws NamingException {
        if (null == me) {
            me = new SessionBeanFactory();
        }
        return me;
    }

    // 判断是否是无状态的sessionBean
    public boolean isStateLessBean(Class clazz) {
        try {
            Class stateless = clazz.getClassLoader().loadClass("javax.ejb.Stateless");
            // 判断是否是无状态对象，是就将其加入本地缓存；
            if (null != stateless && null != clazz.getAnnotation(stateless)) {
                return true;
            }
        } catch (ClassNotFoundException e) {
            return false;
        }
        return false;
    }

    // 得到EJBSessionBean
    private Object getEJB(Class interfaceClazz, Object impl, String key) throws Exception {
        try {
            EjbBeanAccess sessionBean = null;
            // 判断是否是无状态
            if (isStateLessBean(impl.getClass())) {
                sessionBean = AnnotationRuleManager.getInstance().getStateLessBeanMap().get(impl.getClass().getName());
                if (null != sessionBean)
                    return sessionBean;
            } else {
                // 防止循环依赖构造
                if (null != AnnotationRuleManager.getInstance().getStateFulBeanMap().get(key)) {
                    for (EjbBeanAccess stateFulBean : AnnotationRuleManager.getInstance().getStateFulBeanMap().get(key)) {
                        if (null != stateFulBean && impl.getClass().equals(stateFulBean.getBean().getClass())) {
                            return stateFulBean;
                        }
                    }
                }
            }

            // 获取SessionBean
            sessionBean = lookupSessionBean(interfaceClazz, impl);

            // 设置和获取规则集合
            Map<AnnotationRule, RuleParams> mm = getRuleMap(sessionBean , key);

            // 生命周期有状态的INIT
            if (!isStateLessBean(impl.getClass())) {
                for (AnnotationRule rule : mm.keySet()) {
                    // init
                    if (rule instanceof org.knot.mockejb3.annotation.lifeCycle.Init) {
                        RuleParams params = mm.get(rule);
//                        params.setSessionBean(sessionBean);
                        rule.invoke(params);
                    }
                }
            }

            // 生命周期PostConstruct
            for (AnnotationRule rule : mm.keySet()) {
                // postConstruct
                if (rule instanceof org.knot.mockejb3.annotation.lifeCycle.PostConstruct) {
                    RuleParams params = mm.get(rule);
//                    params.setSessionBean(sessionBean);
                    rule.invoke(params);
                }
            }

            // 保存已经构造无状态的sessionBean
            if (isStateLessBean(impl.getClass())) {
                AnnotationRuleManager.getInstance().getStateLessBeanMap().put(impl.getClass().getName(), sessionBean);
            } else // 防止循环依赖构造
            {
                Set<EjbBeanAccess> stateFulBean = AnnotationRuleManager.getInstance().getStateFulBeanMap().get(key);
                if (null == stateFulBean) {
                    stateFulBean = new HashSet<EjbBeanAccess>();
                }
                stateFulBean.add(sessionBean);
                AnnotationRuleManager.getInstance().getStateFulBeanMap().put(key, stateFulBean);
            }

            // 生命周期关联EJB
            for (AnnotationRule rule : mm.keySet()) {
                // EJB
                if (rule instanceof org.knot.mockejb3.annotation.lifeCycle.EJB) {
                    RuleParams params = mm.get(rule);
//                    params.setKey(key);
//                    params.setSessionBean(sessionBean);
                    rule.invoke(params);
                }
            }

            return sessionBean;

        } catch (NamingException e) {
            e.printStackTrace();
            throw e;
        }
    }

    // lookup sessionBean
    private EjbBeanAccess lookupSessionBean(Class interfaceClazz, Object impl) throws NamingException {
        String jndiname = "seller/" + impl.getClass().getSimpleName() + "/local";
        SessionBeanDescriptor sampleServiceDescriptor = new SessionBeanDescriptor(jndiname, interfaceClazz, impl);
        // Deploy operation creates Home and binds it to JNDI
        mockContainer.deploy(sampleServiceDescriptor);

        String packagename = impl.getClass().getPackage().getName();

        
        if (isMockTransaction && -1 == listPointcut.indexOf(packagename)) {
            // 加入事务拦截
            aspectSystem.add(new ClassPatternPointcut(packagename), new TransactionManager(TransactionPolicy.REQUIRED));
            // 处理Remove注解
            if (!isStateLessBean(impl.getClass())) {
            aspectSystem.add(new ClassPatternPointcut(packagename), new StateFulBeanManager());
            }
            listPointcut.add(packagename);
        }
        return (EjbBeanAccess) context.lookup(jndiname);
    }

    // 设置和获取规则集合
    private Map<AnnotationRule, RuleParams> getRuleMap(EjbBeanAccess sessionBean, String key) throws NamingException, InstantiationException,
            IllegalAccessException {

        // 判断此类的注释是否已经获取；
        if (null == AnnotationRuleManager.getInstance().getClasseAnnotations().get(sessionBean)) {
            // 获取Class符合条件的注释
            AnnotationRuleManager.getInstance().createClassAnnotation(sessionBean, key);
        }
        // 生命周期中构造完成后处理
        Map<AnnotationRule, RuleParams> mm = new HashMap<AnnotationRule, RuleParams>();
        ClassAnnotation classAnnotation = AnnotationRuleManager.getInstance().getClasseAnnotations().get(sessionBean);
        if (null != classAnnotation) {
            mm = classAnnotation.getAnnotations();
        }
        return mm;
    }

    /**
     * 获取SessionBean
     * 
     * @param interfaceClazz
     * @param implClass
     * @return
     * @throws Exception
     */
    public  <T> T getEJB(Class<T> interfaceClazz, Class implClass) throws Exception {
        // 如果是无状态且已经存在则直接取出返回；
        if (isStateLessBean(implClass)) {
            Object sessionBean = AnnotationRuleManager.getInstance().getStateLessBeanMap().get(implClass.getName());
            if (null != sessionBean)
                return (T)sessionBean;
        }
        return (T)this.getEJB(interfaceClazz, implClass.newInstance(), java.util.UUID.randomUUID().toString());
    }

    /**
     * 获取SessionBean
     * 
     * @param interfaceClazz
     * @param implClass
     * @return
     * @throws Exception
     */
    public Object getEJB(Class interfaceClazz, Class implClass, String key) throws Exception {
        // 如果是无状态且已经存在则直接取出返回；
        if (isStateLessBean(implClass)) {
            Object sessionBean = AnnotationRuleManager.getInstance().getStateLessBeanMap().get(implClass.getName());
            if (null != sessionBean)
                return sessionBean;
        }
        return this.getEJB(interfaceClazz, implClass.newInstance(), key);
    }
    
    /**
     * 重置Initial
     */
    public void  reverSetAsInitial()
    {
        MockContextFactory.revertSetAsInitial();
    }

    // EBJ销毁
    public void preDestroyEJB() throws Exception {
     //   MockContextFactory.revertSetAsInitial();
        for (Map.Entry<EjbBeanAccess, ClassAnnotation> ejbAnnotation : AnnotationRuleManager.getInstance().getClasseAnnotations()
                .entrySet()) {
            Map<AnnotationRule, RuleParams> mm = ejbAnnotation.getValue().getAnnotations();

            for (AnnotationRule rule : mm.keySet()) {
                // remove
                /*
                 * if (rule instanceof org.mockejb3.annotation.lifeCycle.Remove &&
                 * !isStateLessBean(ejbAnnotation.getKey().getClass())) {
                 * RuleParams params = mm.get(rule);
                 * params.setSessionBean(ejbAnnotation.getKey());
                 * rule.invoke(params); }
                 */

                // PreDestroy
                if (rule instanceof org.knot.mockejb3.annotation.lifeCycle.PreDestroy) {
                    RuleParams params = mm.get(rule);
//                    params.setSessionBean(ejbAnnotation.getKey());
                    rule.invoke(params);
                }
            }
        }
    }

    public void setMockTransaction(boolean isMockTransaction) {
        this.isMockTransaction = isMockTransaction;
    }

}
